package com.example.test.dialogfactory;


import static com.example.test.dialogfactory.MainActivity84.createCameraCapture;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.BuiltinAudioDecoderFactoryFactory;
import org.webrtc.BuiltinAudioEncoderFactoryFactory;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnection.IceServer;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions;
import org.webrtc.RtpReceiver;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

/**
 * PeerConnection
 * PeerConnection也就是Peer-to-Peer connection(P2P), 就是两个"人"的连接.
 * 双方分别创建PeerConnection对象, 然后向对方发送自己的网络状况ICE和多媒体编码格式SDP(因为这时候连接还没建立, 所以发送内容是通过服务器完成的).
 * 当双方网络和编码格式协商好后, 连接就建立好了, 这时从PeerConnection中能获取到对方的MediaStream数据流, 也就能播放对方的音视频了.
 * <p>
 * ICE
 * Interactive Connectivity Establishment, 交互式连接建立. 其实是一个整合STUN和TURN的框架, 给它提供STUN和TURN服务器地址,
 * 它会自动选择优先级高的进行NAT穿透.
 * <p>
 * SDP
 * Session Description Protocol: 会话描述协议. 发送方的叫Offer, 接受方的叫Answer, 除了名字外没有区别. 就是一些文本描述本地的音视频编码和网络地址等.
 * <p>
 * 主要流程
 * A(local)和B(remote)代表两个人, 初始化PeerConnectionFactory并分别创建PeerConnection, 并向PeerConnection添加本地媒体流.
 * <p>
 * A创建Offer
 * A保存Offer(set local description)
 * A发送Offer给B
 * B保存Offer(set remote description)
 * B创建Answer
 * B保存Answer(set local description)
 * B发送Answer给A
 * A保存Answer(set remote description)
 * A发送Ice Candidates给B
 * B发送Ice Candidates给A
 * A,B收到对方的媒体流并播放
 * <p>
 * 作者：rome753
 * 链接：https://www.jianshu.com/p/2b9239eb793b
 * 来源：简书
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */
public class MainActivity85 extends AppCompatActivity {

    PeerConnectionFactory peerConnectionFactory;

    PeerConnection peerConnectionLocal;
    PeerConnection peerConnectionRemote;

    SurfaceViewRenderer localView;
    SurfaceViewRenderer remoteView;

    MediaStream mediaStreamLocal;
    MediaStream mediaStreamRemote;

    public static void start( Context context ) {

        Intent starter = new Intent( context, MainActivity85.class );
        context.startActivity( starter );
    }

    @Override
    protected void onCreate( Bundle savedInstanceState ) {

        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main85 );

        EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();

        localView = findViewById( R.id.localView );
        localView.setMirror( true );
        localView.init( eglBaseContext, null );

        remoteView = findViewById( R.id.backView );
        remoteView.setMirror( false );
        remoteView.init( eglBaseContext, null );

        InitializationOptions initializationOptions = InitializationOptions.builder( this )
            .createInitializationOptions();
        PeerConnectionFactory.initialize( initializationOptions );
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        /* 视频解码 */
        DefaultVideoEncoderFactory defaultVideoEncoderFactory =
            new DefaultVideoEncoderFactory( eglBaseContext, true, true );
        DefaultVideoDecoderFactory defaultVideoDecoderFactory =
            new DefaultVideoDecoderFactory( eglBaseContext );
        /* 音频解码 */
        BuiltinAudioDecoderFactoryFactory audioDecoderFactoryFactory = new BuiltinAudioDecoderFactoryFactory();
        BuiltinAudioEncoderFactoryFactory audioEncoderFactoryFactory = new BuiltinAudioEncoderFactoryFactory();

        /* p2p连接 */
        peerConnectionFactory = PeerConnectionFactory.builder()
            .setOptions( options )
            .setVideoEncoderFactory( defaultVideoEncoderFactory )
            .setVideoDecoderFactory( defaultVideoDecoderFactory )
            .setAudioEncoderFactoryFactory( audioEncoderFactoryFactory )
            .setAudioDecoderFactoryFactory( audioDecoderFactoryFactory )
            .createPeerConnectionFactory();

        /* 本地数据流 */
        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( "CaptureThread", eglBaseContext );
        VideoCapturer videoCapturer = createCameraCapture( true );
        VideoSource videoSource = peerConnectionFactory.createVideoSource( videoCapturer.isScreencast() );
        videoCapturer.initialize( surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver() );
        videoCapturer.startCapture( 480, 640, 30 );
        VideoTrack videoTrack = peerConnectionFactory.createVideoTrack( "100", videoSource );
        mediaStreamLocal = peerConnectionFactory.createLocalMediaStream( "mediaStreamLocal" );
        mediaStreamLocal.addTrack( videoTrack );
        AudioSource audioSource = peerConnectionFactory.createAudioSource( new MediaConstraints() );
        AudioTrack audioTrack = peerConnectionFactory.createAudioTrack( "101", audioSource );
        mediaStreamLocal.addTrack( audioTrack );

        /* 远端数据流 */
        SurfaceTextureHelper remoteSurfaceTextureHelper = SurfaceTextureHelper.create( "RemoteCaptureThread", eglBaseContext );
        VideoCapturer remoteVideoCapturer = createCameraCapture( false );
        VideoSource remoteVideoSource = peerConnectionFactory.createVideoSource( remoteVideoCapturer.isScreencast() );
        remoteVideoCapturer.initialize( remoteSurfaceTextureHelper, getApplicationContext(),
            remoteVideoSource.getCapturerObserver() );
        remoteVideoCapturer.startCapture( 480, 640, 30 );
        VideoTrack remoteVideoTrack = peerConnectionFactory.createVideoTrack( "102", remoteVideoSource );
        mediaStreamRemote = peerConnectionFactory.createLocalMediaStream( "mediaStreamRemote" );
        mediaStreamRemote.addTrack( remoteVideoTrack );

        List< IceServer > iceServers = new ArrayList<>();

        peerConnectionLocal = peerConnectionFactory.createPeerConnection( iceServers,
            new PeerConnectionAdapter( "localconnection" ) {
                @Override
                public void onIceCandidate( IceCandidate iceCandidate ) {

                    super.onIceCandidate( iceCandidate );
                    peerConnectionRemote.addIceCandidate( iceCandidate );
                }

                @Override
                public void onAddStream( MediaStream mediaStream ) {

                    super.onAddStream( mediaStream );
                    VideoTrack remoteVideoTrack = mediaStream.videoTracks.get( 0 );
                    runOnUiThread( () -> {
                        remoteVideoTrack.addSink( localView );
                    } );
                }
            } );

        peerConnectionRemote = peerConnectionFactory.createPeerConnection( iceServers,
            new PeerConnectionAdapter( "remoteconnection" ) {
                @Override
                public void onIceCandidate( IceCandidate iceCandidate ) {

                    super.onIceCandidate( iceCandidate );
                    peerConnectionLocal.addIceCandidate( iceCandidate );
                }

                @Override
                public void onAddStream( MediaStream mediaStream ) {

                    super.onAddStream( mediaStream );
                    VideoTrack localVideoTrack = mediaStream.videoTracks.get( 0 );
                    runOnUiThread( () -> {
                        localVideoTrack.addSink( remoteView );
                    } );
                }
            } );

        peerConnectionLocal.addStream( mediaStreamLocal );
        peerConnectionLocal.createOffer( new SdpAdapter( "local offer sdp" ) {
            @Override
            public void onCreateSuccess( SessionDescription sessionDescription ) {

                super.onCreateSuccess( sessionDescription );

                peerConnectionLocal.setLocalDescription( new SdpAdapter( "local set local" ), sessionDescription );
                peerConnectionRemote.addStream( mediaStreamRemote );
                peerConnectionRemote.setRemoteDescription( new SdpAdapter( "remote set remote" ), sessionDescription );
                peerConnectionRemote.createAnswer( new SdpAdapter( "remote answer sdp" ) {
                    @Override
                    public void onCreateSuccess( SessionDescription sdp ) {

                        super.onCreateSuccess( sdp );
                        peerConnectionRemote.setLocalDescription( new SdpAdapter( "remote set local" ), sdp );
                        peerConnectionLocal.setRemoteDescription( new SdpAdapter( "local set remote" ), sdp );
                    }
                }, new MediaConstraints() );
            }
        }, new MediaConstraints() );
    }

    public static class PeerConnectionAdapter implements PeerConnection.Observer {

        private final String tag;

        public PeerConnectionAdapter( String tag ) {

            this.tag = "chao " + tag;
        }

        private void log( String s ) {

            //Log.d( tag, s );
        }

        @Override
        public void onSignalingChange( PeerConnection.SignalingState signalingState ) {

            log( "onSignalingChange " + signalingState );
        }

        @Override
        public void onIceConnectionChange( PeerConnection.IceConnectionState iceConnectionState ) {

            log( "onIceConnectionChange " + iceConnectionState );
        }

        @Override
        public void onIceConnectionReceivingChange( boolean b ) {

            log( "onIceConnectionReceivingChange " + b );
        }

        @Override
        public void onIceGatheringChange( PeerConnection.IceGatheringState iceGatheringState ) {

            log( "onIceGatheringChange " + iceGatheringState );
        }

        @Override
        public void onIceCandidate( IceCandidate iceCandidate ) {

            log( "onIceCandidate " + iceCandidate );
        }

        @Override
        public void onIceCandidatesRemoved( IceCandidate[] iceCandidates ) {

            log( "onIceCandidatesRemoved " + iceCandidates );
        }

        @Override
        public void onAddStream( MediaStream mediaStream ) {

            log( "onAddStream " + mediaStream );
        }

        @Override
        public void onRemoveStream( MediaStream mediaStream ) {

            log( "onRemoveStream " + mediaStream );
        }

        @Override
        public void onDataChannel( DataChannel dataChannel ) {

            log( "onDataChannel " + dataChannel );
        }

        @Override
        public void onRenegotiationNeeded() {

            log( "onRenegotiationNeeded " );
        }

        @Override
        public void onAddTrack( RtpReceiver rtpReceiver, MediaStream[] mediaStreams ) {

            log( "onAddTrack " + mediaStreams );
        }
    }

    public static class SdpAdapter implements SdpObserver {

        private final String tag;

        public SdpAdapter( String tag ) {

            this.tag = "chao " + tag;
        }

        private void log( String s ) {

            //Log.d( tag, s );
        }

        @Override
        public void onCreateSuccess( SessionDescription sessionDescription ) {

            log( "onCreateSuccess " + sessionDescription );
        }

        @Override
        public void onSetSuccess() {

            log( "onSetSuccess " );
        }

        @Override
        public void onCreateFailure( String s ) {

            log( "onCreateFailure " + s );
        }

        @Override
        public void onSetFailure( String s ) {

            log( "onSetFailure " + s );
        }
    }
}